/**
 * MM32L0xx USB
 * 在APB1上，
 * 
 * MM32L0xx 的USB有1个控制传输端点和4个通用端点可以用于中断传输和批量传输
 * 控制、批量、中断传输，最大包长64字节
 * 
 * 
 * */
//#include "usb_regs.h"
#include <stdint.h>

//USB 寄存器结构
struct usb {
	volatile uint32_t usb_top		;	//0x0000 usb top寄存器
	volatile uint32_t usb_int_state	;	//0x0004 usb中断状态寄存器
	volatile uint32_t ep_int_state	;	//0x0008 usb端点中断状态寄存器
	volatile uint32_t ep0_int_state	;	//0x000c usb端点0中断状态寄存器
	volatile uint32_t usb_int_en	;	//0x0010 usb中断使能寄存器
	volatile uint32_t ep_int_en		;	//0x0014 usb端点中断使能寄存器
	volatile uint32_t ep0_int_en	;	//0x0018 usb端点0中断使能寄存器
	volatile uint32_t usb_resv0		;	//0x001c 保留
	volatile uint32_t epx_int_state[8]	;	//0x0020 - 0x002c 端点x的中断状态，前四个有效
	volatile uint32_t epx_int_en[8]	;	//0x0040 - 0x004c 端点x的中断使能，前四个有效
	volatile uint32_t usb_addr		;	//0x0060 usb地址寄存器
	volatile uint32_t ep_en			;	//0x0064 usb端点使能寄存器
	volatile uint32_t ep_dma_dir	;	//0x0068 -----------编程手册未描述此寄存器!!!!
	volatile uint32_t ep_dma_size	;	//0x006c -----------编程手册未描述此寄存器!!!!
	volatile uint32_t ep_index1_2	;	//0x0070 -----------编程手册未描述此寄存器!!!!
	volatile uint32_t ep_index3_4	;	//0x0074 -----------编程手册未描述此寄存器!!!!
	volatile uint32_t tog_ctrl1_4	;	//0x0078 usb地址寄存器
	volatile uint32_t usb_resv2		;	//0x007c 保留寄存器
	volatile uint32_t setupx[8]		;	//0x0080 - 0x009c usb传输数据包寄存器
	volatile uint32_t pkt_size[2]	;	//0x00a0 - 0x00a4 usb传输包大小寄存器
	volatile uint32_t usb_resv3[22]	;	//0x00a8 - 0x00fc usb保留寄存器
	volatile uint32_t epx_avail[5]	;	//0x0100 - 0x0110 端点x有效数据寄存器
	volatile uint32_t usb_resv4[11]	;	//0x0114 - 0x013c usb保留寄存器
	volatile uint32_t epx_ctrl[5]	;	//0x0140 - 0x0150 usb端点x控制寄存器
	volatile uint32_t usb_resv5[3]	;	//0x0154 - 0x015c usb 保留寄存器
	volatile uint32_t epx_fifo[5]	;	//0x0160 - 0x0170 usb 端点x fifo寄存器
	volatile uint32_t usb_resv6[4]	;	//0x0174 - 0x0180 usb 保留寄存器
	volatile uint32_t ep_dma		;	//0x0184 usb 端点DMA使能寄存器
	volatile uint32_t ep_halt		;	//0x0188 usb端点暂停寄存器
	volatile uint32_t usb_resv7[13]	;	//0x018c - 0x01bc
	volatile uint32_t usb_power		;	//0x01c0 usb功耗控制寄存器
	volatile uint32_t usb_dma		;	//0x01c4 -----------编程手册未描述此寄存器!!!!
	volatile uint32_t usb_reset		;	//0x01c8 -----------编程手册未描述此寄存器!!!!
};


//usb 设备状态
typedef enum{
	USB_ST_ATTACHED 	= 	0,
	USB_ST_POWERED		=	1,
	USB_ST_DEFAULT		=	2,
	USB_ST_ADDRESS		=	3,
	USB_ST_CONF			=	4,
	USB_ST_SUSPENDED	=  	5,
}usb_state_t;

//-----------------------变量定义
#define USB_BASE (struct usb *) 0x40005c00
static usb_state_t slv_usb_state;



//MM32L0xx USB初始化
void usb_init(void)
{
	struct usb *pusb;
	int i;

	pusb = USB_BASE;
	slv_usb_state = USB_ST_POWERED;	//usb设备切换为上电状态

	for(i=0; i < 5; i++){
		pusb->epx_ctrl[i] = 0x00;	//控制端点的传输使能以及长度清零
	}

	pusb->usb_top = 0x80;	//复位，断开连接
	pusb->usb_top = 0x00;	//解除复位，断开连接

	pusb->usb_int_state 	= pusb->usb_int_state & 0x1f	;	//清除usb中断状态
	pusb->ep0_int_state 	= pusb->ep0_int_state & 0xff	;	//清除端点0中断状态
	pusb->epx_int_state[0] 	= pusb->epx_int_state[0] & 0xff	;	//清除端点1的中断状态
	pusb->epx_int_state[1] 	= pusb->epx_int_state[1] & 0xff	;	//清除端点2的中断状态
	pusb->epx_int_state[2] 	= pusb->epx_int_state[2] & 0xff	;	//清除端点3的中断状态
	pusb->epx_int_state[3] 	= pusb->epx_int_state[3] & 0xff	;	//清除端点4的中断状态
	
	//清除控制端点
	for(i=0; i < 5; i++){
		pusb->epx_ctrl[i] = 0x00;	//控制端点的传输使能以及长度清零
	}

	//usb 中断使能配置
	//bit0:总线复位中断
	//bit1:总线挂起中断
	//bit2:总线唤醒中断
	//bit3:sof监测中断
	//bit4:端点中断使能
	//bit7:中断屏蔽位
	//其它位保留 bit5-bit6 数据手册保留位，提供参考代码中有写入0x60,像是初始化DMA相关
	pusb->usb_int_en = 0x01 | 0x02 | 0x04 | 0x10 | 0x60;
	pusb->ep_int_en = 0x1f;
	//ep0中断使能
	//bit0:接收setup包
	//bit1:传输完成中断使能
	//bit2:IN包应答NACK中断使能
	//bit3:IN包应答ACK中断使能
	//bit4:IN包应答STALL中断使能
	//bit5:out包应答NACK中断使能
	//bit6:out包应答ack中断使能
	//bit7:out包应答stall中断使能
	//其它位保留
	pusb->ep0_int_en = 0x01 | 0x02 | 0x04 | 0x40 | 0x90;

	//端点1作为输入端点 端点2作为输出端点
	//bit0: 保留
	//bit1: 传输完成中断使能
	//bit2: IN包应答NACK中断使能
	//bit3: IN包应答ACK中断使能
	//bit4: IN包应答STALL中断使能
	//bit5: out包应答NACK中断使能
	//bit6: out包应答ack中断使能
	//bit7: out包应答stall中断使能
	//其它位保留
	pusb->epx_int_en[0] = 0x02 | 0x04 | 0x90;	//端点1，输入
	pusb->epx_int_en[1] = 0x02 | 0x40 | 0x90;	//端点2，输出

	//端点使能寄存器
	//bit0:使能端点0
	//bit1:使能端点1
	//bit2:使能端点2
	//bit3:使能端点3
	//bit4:使能端点4
	pusb->ep_en = 0x7; //使能端点0,1,2

	//初始化USB设备地址
	pusb->usb_addr = 0;

	//这个寄存器编程手册中未介绍，复制参考工程中的代码
	pusb->ep_dma_dir = 0x01;

	//USB DMA传输的最大包长 64字节
	pusb->pkt_size[0] = 64;
	pusb->pkt_size[1] = 64;

	//这个寄存器编程手册未介绍，复制参考代码，注释为EP1，EP2选择packet size 0
	//介绍说，只有ep1,ep2支持DMA操作
	pusb->ep_dma_size = 0;

	//这个寄存器编程手册中未介绍，复制参考工程中的代码
	//像是dma burst 长度，以及DMA超时
	pusb->usb_dma = 0x70;

	//使用USB DMA
	#if 0
		//bit 0:端点1 DMA使能位
		//bit 1:端点2 DMA使能位
		//其它位保留
		pusb->ep_dma = 0x00;	//禁止DMA
		...
		usb_dma_init();
	#endif

	//bit0: 0:全速 1:低速
	//bit1: 0:断开 1:连接usb
	//bit3: 1:复位usb
	//bit4-6: 只读
	//bit7:usb总线活跃
	pusb->usb_top = 0x02;

	//bit0:总线挂起使能位
	//bit1:0:挂起模式 1：正常模式
	//bit3:0:不唤醒 1:唤醒
	//其它位保留
	pusb->usb_power = 0x01;
	pusb->usb_power = 0x03;

	//usb初始化结束
}

//USB 中断处理函数
//当中断发生后，读取usb 中断寄存器，判别中断发生原因，进行对应中断处理
// 当是端点发生中断，则读取端点中断状态寄存器，判断那个端点发生中断
// 接着读取对应对应端点中断状态寄存，判断是何种类型中断,进行对应中断处理
// USB_INT_STATE -> EP_INT_STATE -> EPx_INT_STATE
//根据最终EPx_INT_STATE调用最终函数进行处理
void USB_IRQHandler(void)
{
	uint32_t tmp;
	uint32_t tmp_ep;
	uint32_t tmp_epn;
	struct usb *pusb;
	pusb = USB_BASE;

	//读取中断状态，并清除中断状态
	tmp = pusb->usb_int_state;
	pusb->ep0_int_state = tmp;

	//根据中断状态进行处理
	//bit0:usb总线复位请求标志
	//bit1:usb总线挂起标志
	//bit2:usb总线唤醒标志
	//bit3:sof监测标志
	//bit4:端点中断标志
	if(tmp & 0x10){	//如果端点产生中断
		//读取端点中断状态寄存器
		tmp_ep = pusb->ep_int_state; //bit0-bit4分别代表端点0-4
		if(tmp_ep & 0x01){ //如果端点0产生中断
			tmp_epn = pusb->ep0_int_state;	//读取端点0中断状态
			pusb->ep0_int_state = tmp_epn;	//清除端点0中断状态
			//bit0:setup中断
			//bit1:传输完成中断
			//bit2: IN包应答NACK中断使能
			//bit3: IN包应答ACK中断使能
			//bit4: IN包应答STALL中断使能
			//bit5: out包应答NACK中断使能
			//bit6: out包应答ack中断使能
			//bit7: out包应答stall中断使能
			if(tmp_epn & 0x01){ //出现setup中断
				usb_setup_handle();
			}
			if(tmp_epn & 0x04){ //接收IN包，准备数据
				usb_in_handle();
				pusb->epx_int_state[0] = 0x04; //清理NACK中断
				pusb->epx_int_en[0] = 0x02 | 0x04 | 0x40 | 0x90; //使能输入端点中断
			}
			if(tmp_epn & 0x40){//如果out包产生ack中断
				usb_out_handle();
			}
			//if(tmp_epn & 0x90){//如果产生stall中断
			//
			//}
		}

		if(tmp_ep & 0x02){//如果端点1产生中断

		}

		if(tmp_ep & 0x04){//如果端点2产生中断
			tmp_epn = pusb->epx_int_state[1];
			pusb->epx_int_state[1] = tmp_epn;	//清除端点中断


		}
		//其它端点...
	}else if(tmp & 0x01){//如果usb总线请求复位
		//执行复位过程
		tmp = pusb->usb_top;
		pusb->usb_top = tmp | 0x08;	//写入复位
		tmp = pusb->usb_top;
		pusb->usb_top = tmp & 0xf7; //解除复位
	}else if(tmp & 0x02){//如果usb总线请求挂起
		if(slv_usb_state > USB_ST_SUSPENDED){
			pusb->usb_power = 0x03;
			slv_usb_state = USB_ST_SUSPENDED;
		}
	}else if(tmp & 0x04){//如果usb总线请求唤醒
		//usb_resume_handle();
	}

}





















